package RPC;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Random;

import BasicDataType.CookieType;
import BasicDataType.IPP;
import cs5300.indexDisplayWrapper;
import cs5300.sessionState;

public class RPCclient implements Runnable {

	private boolean stop = false;
	public static ArrayList<IPP> desList = new ArrayList<IPP>();
	public static int SESSIONTIMEOUT = 5000;

	public static void addMemberList(IPP address) {
		boolean hasCookie = false;
		if (address == null) {
			return;
		}
		if (address.getString().equals(RPCserver.serverAddress.getString())) {
			return;
		}
		for (int i = 0; i < desList.size(); i++) {
			if (desList.get(i).getString().equals(address.getString())) {
				return;
			}
		}
		if (!hasCookie) {
			desList.add(address);
		}

	}

	public static IPP getRandomServer() {
		Random generator = new Random();
		if (desList.size() == 0) {
			return null;
		}
		return desList.get(generator.nextInt(desList.size()));
	}

	public RPCclient() {
		// load the list of IPPs to desList
	}

	private ResponseMessage recievePacket(DatagramSocket clientSocket, String callID) throws IOException {
		byte[] receiveData = new byte[1024];
		DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
		ResponseMessage resM;
		do {
			clientSocket.receive(receivePacket);
			String response = new String(receivePacket.getData());
			resM = new ResponseMessage(response);
		} while (!resM.callID.equals(callID));
		return resM;
	}

	public boolean sessionWrite(sessionState ss) {

		ss.cookie.ippPrimary = RPCserver.serverAddress;

		if (ss.cookie.backup == null) {
			ss.cookie.backup = getRandomServer();
			if (ss.cookie.backup == null) {
				return false;
			}
		}

		// set a request
		RequestMessage reqM = new RequestMessage();
		reqM.opeID = RPCConstants.SESSIONWRITE;
		reqM.ss = ss;
		// reqM.cookie = ss.cookie;
		// reqM.discardTime = ss.discard_time;

		try {
			DatagramSocket clientSocket;
			clientSocket = new DatagramSocket();
			clientSocket.setSoTimeout(SESSIONTIMEOUT);
			byte[] sendData = new byte[1024];
			sendData = reqM.getString().getBytes();
			DatagramPacket dgp = new DatagramPacket(sendData, sendData.length, ss.cookie.backup.IPaddress, ss.cookie.backup.Port);
			clientSocket.send(dgp);
			indexDisplayWrapper.evict += reqM.getString() + "------>";
			ResponseMessage resM = recievePacket(clientSocket, reqM.callID);
			// check if success, if not then try another
			DatagramPacket packet;
			int index = 0;
			while (!resM.result.equals(RPCConstants.SUCCESS) && (!desList.isEmpty())) {
				desList.remove(reqM.ss.cookie.backup);
				reqM.ss.cookie.backup = desList.get(index);
				sendData = reqM.getString().getBytes();
				InetAddress address = desList.get(index).IPaddress;
				int port = desList.get(index).Port;
				packet = new DatagramPacket(sendData, sendData.length, address, port);
				clientSocket.send(dgp);
				indexDisplayWrapper.evict += reqM.getString();
				resM = recievePacket(clientSocket, reqM.callID);
				String modifiedSentence = new String(resM.getString());
				System.out.println("FROM SERVER:" + modifiedSentence);
			}

			// save at the local table
			if (desList.isEmpty()) {
				ss.cookie.backup = null;
				return false;
			}

		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
			indexDisplayWrapper.evict += "time Out le";
			return false;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return true;
	}

	public boolean sessionDelete(CookieType cookie) {
		try {
			RequestMessage reqM = new RequestMessage();
			reqM.opeID = RPCConstants.SESSIONDELETE;
			reqM.ss.cookie = cookie;
			DatagramSocket clientSocket;
			clientSocket = new DatagramSocket();
			byte[] sendData = new byte[1024];
			sendData = reqM.getString().getBytes();
			for (int i = 0; i < desList.size(); i++) {
				InetAddress address = desList.get(i).IPaddress;
				int port = desList.get(i).Port;
				DatagramPacket dgp = new DatagramPacket(sendData, sendData.length, address, port);
				clientSocket.send(dgp);
			}

		} catch (IOException e) {
			return false;
		}

		return true;
	}

	public DatagramPacket sessionRead(CookieType cookie) {
		try {
			if (desList.isEmpty()) {
				return null;
			}
			RequestMessage rm = new RequestMessage(RPCConstants.SESSIONREAD, cookie, 0);
			DatagramSocket clientSocket;
			clientSocket = new DatagramSocket();
			clientSocket.setSoTimeout(SESSIONTIMEOUT);
			for (int i = 0; i < desList.size(); i++) {
				byte[] sendData = new byte[1024];
				sendData = rm.getString().getBytes();
				InetAddress desAddress = desList.get(i).IPaddress;
				int port = desList.get(i).Port;
				DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, desAddress, port);
				try {

					clientSocket.send(sendPacket);
					indexDisplayWrapper.evict += rm.getString();
					System.out.println(rm.getString());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return null;
				}
			}
			byte[] receiveData = new byte[1024];
			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
			ResponseMessage resM;
			do {
				clientSocket.receive(receivePacket);
				String response = new String(receivePacket.getData());
				resM = new ResponseMessage(response);
			} while (!resM.callID.equals(rm.callID));
			String modifiedSentence = new String(receivePacket.getData());
			System.out.println("FROM SERVER:" + modifiedSentence);
			clientSocket.close();
			return receivePacket;
		} catch (InterruptedIOException e) {
			return null;
		} catch (IOException e) {
			System.out.println("time out la");
			indexDisplayWrapper.evict += " READ time Out le";
			return null;
		}
	}

	public static void sessionPing(CookieType cookie) {
		System.out.println("Session ping send");
		desList.clear();
		SimpleDB db = new SimpleDB();
		desList.addAll(db.getList());
		db.clear();
		try {
			RequestMessage rm = new RequestMessage(RPCConstants.SESSIONPING, cookie, 0);
			DatagramSocket clientSocket;
			clientSocket = new DatagramSocket();
			clientSocket.setSoTimeout(5 * 1000);
			for (int i = 0; i < desList.size(); i++) {
				byte[] sendData = new byte[1024];
				sendData = rm.getString().getBytes();
				InetAddress desAddress = desList.get(i).IPaddress;
				int port = desList.get(i).Port;
				DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, desAddress, port);

				try {
					clientSocket.send(sendPacket);
				} catch (IOException e) {
					e.printStackTrace();
				}
				byte[] receiveData = new byte[1024];
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				ResponseMessage resM;
				do {
					clientSocket.receive(receivePacket);
					String response = new String(receivePacket.getData());
					resM = new ResponseMessage(response);
				} while (!resM.callID.equals(rm.callID));

				if (resM.result.equals("PingReceived"))// if received, update
														// its IPP to DB
					db.update(desList.get(i));
			}
			clientSocket.close();
		} catch (InterruptedIOException e) {
			return;
			// TODO: handle exception
		} catch (IOException e) {
			e.printStackTrace();
			return;
			// TODO Auto-generated catch block
		}
		desList.clear();
		desList.addAll(db.getList());
	}

	public void stop() {
		stop = true;
	}

	@Override
	public void run() {

	}

}
